package com.example.hello;

import com.example.hello.client.Button.BootstrapButtonRpc;
import com.example.hello.client.Button.BootstrapButtonState;
import com.vaadin.event.ShortcutListener;
import com.vaadin.shared.MouseEventDetails;
import com.vaadin.ui.AbstractComponent;
import com.vaadin.ui.Button;
import com.vaadin.ui.Component;

public class BootstrapButton extends AbstractComponent {

    private BootstrapButtonRpc rpc = new BootstrapButtonRpc() {
        public void click(MouseEventDetails details) {
            fireEvent(new BootstrapEvent(BootstrapButton.this));
        }
    };

    public enum Size {
        LARGE, DEFAULT, SMALL, MINI
    }

    public enum Type {
        DEFAULT, PRIMARY, INFO, SUCCESS, WARNING, DANGER, INVERSE
    }

    public BootstrapButton() {
        this("", Type.DEFAULT, Size.DEFAULT);
    }

    public BootstrapButton(String caption, Size size) {
        this(caption, Type.DEFAULT, size);
    }

    public BootstrapButton(String caption, Type type) {
        this(caption, type, Size.DEFAULT);
    }

    public BootstrapButton(String caption, Type type, Size size) {
        setSize(size);
        setText(caption);
        setType(type);

        setBlock(false);
        setEnabled(true);
        setToggle(false);

        setImmediate(true);

        registerRpc(rpc);
    }

    public BootstrapButton(String caption) {
        this(caption, Size.DEFAULT);
    }

    public BootstrapButton(Size size) {
        this("", size);
    }

    public BootstrapButton(Type type) {
        this("", type);
    }

    @Override
    protected BootstrapButtonState getState() {
        return (BootstrapButtonState) super.getState();
    }

    public boolean isBlock() {
        return getState().isBlock();
    }

    public void setBlock(boolean block) {
        getState().setBlock(block);
    }

    public boolean isEnabled() {
        return getState().isButtonEnabled();
    }

    public void setEnabled(boolean enabled) {
        getState().setButtonEnabled(enabled);
    }

    public Size getSize() {
        return Size.valueOf(getState().getSize());
    }

    public void setSize(Size size) {
        getState().setSize(size.toString());
    }

    public String getText() {
        return getState().getText();
    }

    public void setText(String text) {
        getState().setText(text);
    }

    public boolean isToggle() {
        return getState().isToggle();
    }

    public void setToggle(boolean toggle) {
        getState().setToggle(toggle);
    }

    public Type getType() {
        return Type.valueOf(getState().getType());
    }

    public void setType(Type type) {
        getState().setType(type.toString());
    }

    protected void fireClick() {
        fireEvent(new BootstrapButton.ClickEvent(this));
    }

    protected void fireClick(MouseEventDetails details) {
        fireEvent(new Button.ClickEvent(this, details));
    }

    public void click() {
        if (isEnabled() && !isReadOnly()) fireClick();
    }

    public static class ClickEvent extends Component.Event {

        private final MouseEventDetails details;

        /**
         * New instance of text change event.
         *
         * @param source the Source of the event.
         */
        public ClickEvent(Component source) {
            super(source);
            details = null;
        }

        /**
         * Constructor with mouse details
         *
         * @param source  The source where the click took place
         * @param details Details about the mouse click
         */
        public ClickEvent(Component source, MouseEventDetails details) {
            super(source);
            this.details = details;
        }

        /**
         * Gets the BootstrapButton where the event occurred.
         *
         * @return the Source of the event.
         */
        public Button getButton() {
            return (Button) getSource();
        }

        /**
         * Returns the mouse position (x coordinate) when the click took place.
         * The position is relative to the browser client area.
         *
         * @return The mouse cursor x position or -1 if unknown
         */
        public int getClientX() {
            if (null != details) {
                return details.getClientX();
            } else {
                return -1;
            }
        }

        /**
         * Returns the mouse position (y coordinate) when the click took place.
         * The position is relative to the browser client area.
         *
         * @return The mouse cursor y position or -1 if unknown
         */
        public int getClientY() {
            if (null != details) {
                return details.getClientY();
            } else {
                return -1;
            }
        }

        /**
         * Returns the relative mouse position (x coordinate) when the click
         * took place. The position is relative to the clicked component.
         *
         * @return The mouse cursor x position relative to the clicked layout
         * component or -1 if no x coordinate available
         */
        public int getRelativeX() {
            if (null != details) {
                return details.getRelativeX();
            } else {
                return -1;
            }
        }

        /**
         * Returns the relative mouse position (y coordinate) when the click
         * took place. The position is relative to the clicked component.
         *
         * @return The mouse cursor y position relative to the clicked layout
         * component or -1 if no y coordinate available
         */
        public int getRelativeY() {
            if (null != details) {
                return details.getRelativeY();
            } else {
                return -1;
            }
        }

        /**
         * Checks if the Alt key was down when the mouse event took place.
         *
         * @return true if Alt was down when the event occured, false otherwise
         * or if unknown
         */
        public boolean isAltKey() {
            if (null != details) {
                return details.isAltKey();
            } else {
                return false;
            }
        }

        /**
         * Checks if the Ctrl key was down when the mouse event took place.
         *
         * @return true if Ctrl was pressed when the event occured, false
         * otherwise or if unknown
         */
        public boolean isCtrlKey() {
            if (null != details) {
                return details.isCtrlKey();
            } else {
                return false;
            }
        }

        /**
         * Checks if the Meta key was down when the mouse event took place.
         *
         * @return true if Meta was pressed when the event occured, false
         * otherwise or if unknown
         */
        public boolean isMetaKey() {
            if (null != details) {
                return details.isMetaKey();
            } else {
                return false;
            }
        }

        /**
         * Checks if the Shift key was down when the mouse event took place.
         *
         * @return true if Shift was pressed when the event occured, false
         * otherwise or if unknown
         */
        public boolean isShiftKey() {
            return null != details && details.isShiftKey();
        }
    }

    protected ClickShortcut clickShortcut;

    /**
     * Makes it possible to invoke a click on this button by pressing the given
     * {@link com.vaadin.event.ShortcutAction.KeyCode} and (optional) {@link com.vaadin.event.ShortcutAction.ModifierKey}s.<br/>
     * The shortcut is global (bound to the containing Window).
     *
     * @param keyCode   the keycode for invoking the shortcut
     * @param modifiers the (optional) modifiers for invoking the shortcut, null for
     *                  none
     */
    public void setClickShortcut(int keyCode, int... modifiers) {
        if (clickShortcut != null) {
            removeShortcutListener(clickShortcut);
        }
        clickShortcut = new ClickShortcut(this, keyCode, modifiers);
        addShortcutListener(clickShortcut);
        getState().clickShortcutKeyCode = clickShortcut.getKeyCode();
    }

    /**
     * Removes the keyboard shortcut previously set with
     * {@link #setClickShortcut(int, int...)}.
     */
    public void removeClickShortcut() {
        if (clickShortcut != null) {
            removeShortcutListener(clickShortcut);
            clickShortcut = null;
            getState().clickShortcutKeyCode = 0;
        }
    }

    public static class ClickShortcut extends ShortcutListener {
        protected BootstrapButton button;

        /**
         * Creates a keyboard shortcut for clicking the given button using the
         * shorthand notation defined in {@link com.vaadin.event.ShortcutAction}.
         *
         * @param button           to be clicked when the shortcut is invoked
         * @param shorthandCaption the caption with shortcut keycode and modifiers indicated
         */
        public ClickShortcut(BootstrapButton button, String shorthandCaption) {
            super(shorthandCaption);
            this.button = button;
        }

        /**
         * Creates a keyboard shortcut for clicking the given button using the
         * given {@link KeyCode} and {@link ModifierKey}s.
         *
         * @param button    to be clicked when the shortcut is invoked
         * @param keyCode   KeyCode to react to
         * @param modifiers optional modifiers for shortcut
         */
        public ClickShortcut(BootstrapButton button, int keyCode, int... modifiers) {
            super(null, keyCode, modifiers);
            this.button = button;
        }

        /**
         * Creates a keyboard shortcut for clicking the given button using the
         * given {@link KeyCode}.
         *
         * @param button  to be clicked when the shortcut is invoked
         * @param keyCode KeyCode to react to
         */
        public ClickShortcut(BootstrapButton button, int keyCode) {
            this(button, keyCode, null);
        }

        @Override
        public void handleAction(Object sender, Object target) {
            button.click();
        }
    }

}